package com.toman.Util;

import com.alibaba.fastjson.JSONArray;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: shaojx
 * @Date: 2018/9/12 11:19
 * @Description:
 */
public class JsonExcludeNull extends JSONArray {

    /**
     * privateClass-->WrapperClass
     */
    private static final Map<Class, Class> PRIVATE_WRAPPER_CLASS_MAP = new HashMap<>();
    private static final Set<Class> SPECIAL_NOT_PARSE_CLASS = new HashSet<>();

    private static final ConcurrentHashMap<Class, List<Field>> CACHED_CLASS_FIELDS = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<Class, Object> CACHE_INSTANCES = new ConcurrentHashMap<>();

    static {
        PRIVATE_WRAPPER_CLASS_MAP.put(int.class, Integer.class);
        PRIVATE_WRAPPER_CLASS_MAP.put(short.class, Short.class);
        PRIVATE_WRAPPER_CLASS_MAP.put(byte.class, Byte.class);
        PRIVATE_WRAPPER_CLASS_MAP.put(long.class, Long.class);
        PRIVATE_WRAPPER_CLASS_MAP.put(float.class, Float.class);
        PRIVATE_WRAPPER_CLASS_MAP.put(double.class, Double.class);
        PRIVATE_WRAPPER_CLASS_MAP.put(boolean.class, Boolean.class);
        PRIVATE_WRAPPER_CLASS_MAP.put(BigDecimal.class, BigDecimal.class);


        SPECIAL_NOT_PARSE_CLASS.add(String.class);
        SPECIAL_NOT_PARSE_CLASS.add(BigDecimal.class);
        SPECIAL_NOT_PARSE_CLASS.add(Date.class);
        SPECIAL_NOT_PARSE_CLASS.add(java.sql.Date.class);
        SPECIAL_NOT_PARSE_CLASS.add(ArrayList.class);
        SPECIAL_NOT_PARSE_CLASS.add(List.class);
        SPECIAL_NOT_PARSE_CLASS.add(Map.class);
        SPECIAL_NOT_PARSE_CLASS.add(HashMap.class);
    }

    public static final <T> List<T> parseArrayExcludeNull(String text, Class<T> clazz) {
        List<T> list = parseArray(text, clazz);

        if (list == null || list.size() <= 0) {
            return new ArrayList<>(1);
        } else {
            List<Field> fieldList = CACHED_CLASS_FIELDS.get(clazz);
            if (fieldList == null) {
                collectAllClassFields(clazz);
                doConvertNull2Instance(list, CACHED_CLASS_FIELDS.get(clazz));
            } else {
                doConvertNull2Instance(list, fieldList);
            }
        }

        return list;
    }

    private static <T> void doConvertNull2Instance(List<T> list, List<Field> fieldList) {
        for (T t : list) {
            for (Field field : fieldList) {
                try {
                    Object o = field.get(t);
                    if (o == null) {
                        Class<?> fieldType = field.getType();
                        if (CACHE_INSTANCES.get(fieldType) == null) {
                            CACHE_INSTANCES.put(fieldType, fieldType.newInstance());
                            field.set(t, CACHE_INSTANCES.get(fieldType));
                        } else {
                            field.set(t, CACHE_INSTANCES.get(fieldType));
                        }
                    } else {
                        List<Field> fields = CACHED_CLASS_FIELDS.get(o.getClass());
                        doConvertNull2InstanceForObject(o, fields);
                    }
                } catch (InstantiationException | IllegalAccessException exception) {
                    //pass
                }
            }
        }
    }

    private static <T> void doConvertNull2InstanceForObject(Object targetObject, List<Field> fieldList) {
        for (Field field : fieldList) {
            try {
                Object o = field.get(targetObject);
                if (o == null) {
                    Class<?> fieldType = field.getType();
                    if (CACHE_INSTANCES.get(fieldType) == null) {
                        CACHE_INSTANCES.put(fieldType, fieldType.newInstance());
                        field.set(targetObject, CACHE_INSTANCES.get(fieldType));
                    } else {
                        field.set(targetObject, CACHE_INSTANCES.get(fieldType));
                    }
                } else {
                    //添加过滤
                    if (o.toString().equalsIgnoreCase("NAN") && (PRIVATE_WRAPPER_CLASS_MAP.containsKey(o.getClass()) || PRIVATE_WRAPPER_CLASS_MAP.containsValue(o.getClass()))) {
                        field.set(targetObject, null);
                    }
                   doConvertNull2InstanceForObject(o,CACHED_CLASS_FIELDS.get(o.getClass()));
                }
            } catch (InstantiationException | IllegalAccessException exception) {
                //pass
            }
        }
    }


    private static <T> void collectAllClassFields(Class<T> targetClazz) {
        Objects.requireNonNull(targetClazz);
        if (targetClazz == Object.class) {
            //return
            return;
        }
        //先解析一层
        List<Class> parentFields = new ArrayList<>();

        List<Field> result = new ArrayList<>();
        CACHED_CLASS_FIELDS.put(targetClazz, result);
        doParse(targetClazz, result, parentFields);

        //再解析嵌套类
        int index = 0;
        while (index != parentFields.size()) {
            Class aClass = parentFields.get(index);
            List<Field> resultChild = new ArrayList<>();
            CACHED_CLASS_FIELDS.put(aClass, resultChild);
            doParse(aClass, resultChild, parentFields);
            index++;
        }

    }

    private static <T> void doParse(Class<T> targetClazz, List<Field> result, List<Class> parentFields) {
        Objects.requireNonNull(targetClazz);
        if (targetClazz == Object.class) {
            //return
            return;
        }
        try {
            Field[] declaredFields = targetClazz.getDeclaredFields();
            if (declaredFields != null && declaredFields.length > 0) {
                for (Field declaredField : declaredFields) {
                    Class<?> declaredFieldType = declaredField.getType();
                    if (SPECIAL_NOT_PARSE_CLASS.contains(declaredFieldType) || PRIVATE_WRAPPER_CLASS_MAP.containsKey(declaredFieldType)
                            || PRIVATE_WRAPPER_CLASS_MAP.containsValue(declaredFieldType)) {
                        //pass
                    } else {
                        parentFields.add(declaredFieldType);

                        declaredField.setAccessible(true);
                        result.add(declaredField);
                    }
                }
            }
            doParse(targetClazz.getSuperclass(), result, parentFields);
        } catch (Exception e) {
            //pass
        }
    }


}
